/**
 ******************************************************************************
 * @file      lwip.c
 * @brief     LwIP协议栈初始化代码
 * @author    Gemini
 * @date      2025-09-22
 * @version   1.0
 *
 * @attention
 *
 * 本文件整合了LwIP的初始化流程，可通过 USE_DHCP 宏在静态IP和DHCP模式
 * 之间进行切换。
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "lwip_demo.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "lwip/dhcp.h" // 包含DHCP客户端头文件
#include "ethernetif.h"
#include "malloc_ex.h"
#include <string.h>

#include "network.h"
#include "printf_rtos.h"

#if defined(__CC_ARM) /* MDK ARM Compiler */
#include "lwip/sio.h"
#endif

/* Private Defines -----------------------------------------------------------*/

/**
 * @brief IP地址获取方式选择
 * @note  设置为 1 使用DHCP, 设置为 0 使用静态IP地址。
 */
#define USE_DHCP 1

/**
 * @brief 网络接口监控线程的堆栈大小
 */
#define INTERFACE_THREAD_STACK_SIZE (1024)

/* Private function prototypes -----------------------------------------------*/
static void ethernet_link_status_updated(struct netif *netif);

/* Global Variables ----------------------------------------------------------*/

// 网络接口结构体
struct netif gnetif;

// IP地址相关变量
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gw;

/* LwIP Initialization Function ----------------------------------------------*/

/**
 * @brief LwIP协议栈初始化函数
 * @details
 * 此函数负责初始化TCP/IP核心线程，根据USE_DHCP宏的配置来设定网络
 * 接口的IP地址（静态或DHCP），添加并使能网络接口，并创建一个用于
 * 监控以太网物理链路状态的线程。
 * @param None
 * @retval None
 */
uint8_t *lwip_heap_ext=NULL;
void my_lwip_config(void)
{
    lwip_heap_ext = (uint8_t*)mymalloc(SRAMEX, (32 * 1024));
    /* 使用RTOS初始化LwIP协议栈 (创建tcpip_thread线程) */
    tcpip_init(NULL, NULL);

#if USE_DHCP
    /* DHCP模式: 将IP地址清零，以便DHCP客户端分配 */
    ip4_addr_set_zero(&ipaddr);
    ip4_addr_set_zero(&netmask);
    ip4_addr_set_zero(&gw);
#else
    /* 静态IP模式: 配置固定的IP地址、子网掩码和网关 */
    uint8_t IP_ADDRESS[4] = {10, 0, 0, 176};
    uint8_t NETMASK_ADDRESS[4] = {255, 255, 255, 0};
    uint8_t GATEWAY_ADDRESS[4] = {10, 0, 0, 1};

    IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
    IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1], NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
    IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
#endif /* USE_DHCP */

    /* 添加网络接口 */
    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);

    // 设置设备主机名，此名称将显示在路由器客户端列表中
    netif_set_hostname(&gnetif, "STM32-F407");

    /* 注册为默认网络接口 */
    netif_set_default(&gnetif);

    /* 启用网络接口 (lwip flags: NETIF_FLAG_UP) */
    netif_set_up(&gnetif);

    /* 设置链路状态变化时的回调函数 */
    netif_set_link_callback(&gnetif, ethernet_link_status_updated);

    /* 创建以太网链路监控线程 */
    osThreadAttr_t attributes = {0};
    attributes.name = "EthLink";
    attributes.stack_size = INTERFACE_THREAD_STACK_SIZE;
    attributes.priority = osPriorityBelowNormal;
    osThreadNew(ethernet_link_thread, &gnetif, &attributes);

#if USE_DHCP
    /* 如果使用DHCP，则启动DHCP客户端 */
    dhcp_start(&gnetif);
#endif /* USE_DHCP */
}

/* Callback & Utility Functions ----------------------------------------------*/

/**
 * @brief 网络接口链路状态更新回调函数
 * @note  当 `netif_is_link_up()` 状态改变时，由LwIP内核调用。
 * 您可以在此处理网络断开和重连的逻辑。
 * @param netif a pointer to the network interface
 * @retval None
 */
static void ethernet_link_status_updated(struct netif *netif)
{
    if (netif_is_up(netif))
    {
        // 网络接口已连接
        printf_rtos("eth conn\n");
    }
    else
    {
        printf_rtos("eth disconn\n");
    }
}

#if defined(__CC_ARM) /* MDK ARM Compiler */

/**
 * @brief 打开一个串口设备进行通信 (用于LwIP拨号等功能)。
 * @param devnum device number
 * @return handle to serial device if successful, NULL otherwise
 */
sio_fd_t sio_open(u8_t devnum)
{
    sio_fd_t sd;
    sd = 0; // 虚拟实现
    return sd;
}

/**
 * @brief 向串口发送一个字符。
 * @param c character to send
 * @param fd serial device handle
 */
void sio_send(u8_t c, sio_fd_t fd)
{
    // 虚拟实现
}

/**
 * @brief 从串口读取数据 (阻塞式)。
 * @param fd serial device handle
 * @param data pointer to data buffer for receiving
 * @param len maximum length (in bytes) of data to receive
 * @return number of bytes actually received
 */
u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
{
    u32_t recved_bytes;
    recved_bytes = 0; // 虚拟实现
    return recved_bytes;
}

/**
 * @brief 尝试从串口读取数据 (非阻塞式)。
 * @param fd serial device handle
 * @param data pointer to data buffer for receiving
 * @param len maximum length (in bytes) of data to receive
 * @return number of bytes actually received
 */
u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len)
{
    u32_t recved_bytes;
    recved_bytes = 0; // 虚拟实现
    return recved_bytes;
}

#endif /* MDK ARM Compiler */